/*
 * PuyoWindow.java
 *
 * Created on August 2, 2004 , PM 10:27
 */

/**package name of Puyo game*/
package Puyo;

//import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import java.io.InputStream;

/**
 * Puyo's Main Window, manage user interfaces such as keystroke events, image rendering
 * @author  Ming
 */
public class PuyoWindow extends GameCanvas implements Runnable{
    
  boolean showGameOver = false;
  
  boolean paused = false;
  
  /**contains game data and game logic*/
  PuyoLogic puyoLogic = null;

  /** 4 Puyo images, They are red, green, yellow, blue*/
  public Image puyoImages[];
  
  /*controlling the game speed. the smaller, the faster*/
  private int timeInterval = 200;
 
  /**speed change rate control*/
  double speedChangeRate = 1.25;

  private volatile Thread thread;

  /**an observer which is interested in the event sent by this class*/
  private Observer observer = null;
  
  private PuyoConfig puyoConfig; //reference to the puyoConfig in PuyoMIDlet
  
  private Font smallBoldFont;
  private Font smallFont;
  
  ///player which plays the midi background music
  private Player player;
  
  ///player which plays sound effect after a block is removed
  private Player wavPlayer;
  
  /**constructor*/
  public PuyoWindow() {
     super(false); 
     PuyoSettings.init(this);
     smallBoldFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL);
     smallFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
     puyoImages = new Image[4];
     try{         
           puyoImages[0] = ScaleImage.scaleImage(
                   Image.createImage("/Puyo/images/puyo_red.png"),PuyoSettings.ballWidth,PuyoSettings.ballHeight);
           puyoImages[1] = ScaleImage.scaleImage(
                   Image.createImage("/Puyo/images/puyo_green.png"),PuyoSettings.ballWidth,PuyoSettings.ballHeight);
           puyoImages[2] = ScaleImage.scaleImage(
                   Image.createImage("/Puyo/images/puyo_blue.png"),PuyoSettings.ballWidth,PuyoSettings.ballHeight);
           puyoImages[3] = ScaleImage.scaleImage(
                   Image.createImage("/Puyo/images/puyo_yellow.png"),PuyoSettings.ballWidth,PuyoSettings.ballHeight);
     } catch (java.io.IOException exception) {
          exception.printStackTrace();
     }
  }

  public void Init(PuyoConfig config) {
     puyoLogic = new PuyoLogic();  
     puyoConfig = config;
     showGameOver = false;
     paused = false;
     timeInterval = 200;
     //if pause/resume has been removed after "game over", we need to add pause again
     removeCommand(PuyoMIDlet.pauseCommand);
     removeCommand(PuyoMIDlet.resumeCommand);
     addCommand(PuyoMIDlet.pauseCommand); 
     try {
        closeSound();
        InputStream is = getClass().getResourceAsStream("/Puyo/sounds/Background.mid");
        player = Manager.createPlayer(is,"audio/midi");
        player.prefetch(); // prefetch
        player.realize(); // realize
        
        VolumeControl vc;
        vc = (VolumeControl) player.getControl("VolumeControl");
        vc.setLevel(puyoConfig.getSoundVolume());
        player.setLoopCount(-1); // play indefinitely
        
        is = getClass().getResourceAsStream("/Puyo/sounds/laser.wav");
        wavPlayer = Manager.createPlayer(is,"audio/x-wav");
        if(wavPlayer == null) //second chance
            wavPlayer = Manager.createPlayer(is,"audio/wav");
        if(wavPlayer != null){
            wavPlayer.prefetch(); // prefetch
            wavPlayer.realize(); // realize

            vc = (VolumeControl) wavPlayer.getControl("VolumeControl");
            vc.setLevel(puyoConfig.getSoundVolume());
            wavPlayer.setLoopCount(1); // play only once
        }
        
        player.start(); // start the background music
    } catch (Exception ex) {
        closeSound();
    }    
  }
  
  /**tune up game speed*/
  public void speedUp(){
     timeInterval /= speedChangeRate;
  }
    
  /**tune down game speed*/
  public void speedDown(){
     timeInterval *= speedChangeRate;
  }
 
  public void keyPressed(int keyCode) {
      if( this.paused )
          return;
      switch (getGameAction(keyCode)) {
	case Canvas.UP:
	   puyoLogic.rotateCW();
	    break;
	case Canvas.DOWN:
	     puyoLogic.rotateCCW();
	    break;
	case Canvas.LEFT:
	    puyoLogic.moveLeft();
	    break;
	case Canvas.RIGHT:
	    puyoLogic.moveRight();
	    break;
        default:
	    // There is no game action.. Use keypad constants instead
	    switch (keyCode) {
	    case Canvas.KEY_NUM2:
		puyoLogic.rotateCW();
		break;
	    case Canvas.KEY_NUM8:
		puyoLogic.rotateCCW();
		break;
	    case Canvas.KEY_NUM4:
		puyoLogic.moveLeft();
		break;
	    case Canvas.KEY_NUM6:
		puyoLogic.moveRight();
		break;
            case Canvas.KEY_NUM1:
                changeVolume(-20);
                break;
            case Canvas.KEY_NUM3:
                changeVolume(20);
                break;    
            case KEY_STAR:
                speedUp();
                break;
            case KEY_POUND:
                speedDown();
                break;       
	    }
	    break;
	}
   }

  /**draw puyo images according to the stored game data*/
  public void updateGameScreen(Graphics g){
     // clear background 
     g.setColor(0xffffff);
     g.fillRect(0,0,PuyoSettings.canvasWidth,PuyoSettings.canvasHeight);            
     
     // draw a frame enclosing the board
     g.setColor(0xff0000); 
     g.drawRect(0,0,PuyoSettings.boardWidthInPixel() + 1,
                     PuyoSettings.boardHeightInPixel() + 1 );
     
     // draw the balls
     g.translate(1,1); //translate the coordsys so that the balls are not overlapping with the frame
     drawBalls(g); 
     g.translate(0,0); //restore the coordsys
     int floorThickness = PuyoSettings.floorThickness();
     if(floorThickness > 0){
       g.setColor(0x0000ff); //blue floor
       g.fillRect(0,PuyoSettings.boardHeightInPixel(),
                   PuyoSettings.canvasWidth,floorThickness);
     }

     if(showGameOver){
      g.setColor(0xff0000);
      g.drawString("GameOver",PuyoSettings.canvasWidth/2,PuyoSettings.canvasHeight*2/3, 
                    Graphics.TOP | Graphics.LEFT);    
     }
     
     if(puyoLogic != null){
      g.setColor(0xff00ff);
      g.setFont(smallBoldFont);
      int nMaxWidth = g.getFont().charsWidth("PlaceHolder".toCharArray(),0,11);
      int nStart = PuyoSettings.canvasWidth - nMaxWidth;
      
      g.drawString("Player:",nStart,10, Graphics.TOP | Graphics.LEFT); 
      g.drawString("Score:",nStart,60, Graphics.TOP | Graphics.LEFT); 
      g.drawString("Volume:",nStart,110, Graphics.TOP | Graphics.LEFT);
      
      g.setFont(smallFont);
      g.drawString(puyoConfig.getCurrentPlayerName(),nStart,26, Graphics.TOP | Graphics.LEFT);      
      g.drawString(String.valueOf(puyoLogic.getScore()),nStart,76, Graphics.TOP | Graphics.LEFT);   
      int volumeControlWidth = ((VolumeControl) player.getControl("VolumeControl")).getLevel()*2/5;
      if(volumeControlWidth != 0)
          g.fillRect(nStart, 126 , volumeControlWidth , 10 );
      else {
          //draw a mute sign
          g.fillRect(nStart, 126 + 3, 4, 5);
          g.fillTriangle(nStart, 131, nStart + 8, 124, nStart + 8, 138);
          g.drawLine(nStart, 136, nStart + 10, 126);
      }
      g.setFont(Font.getDefaultFont());
     }
     flushGraphics();

  }
  
  private int color2ImageIndex(int color){
      return color-1;
  }
  
  private void drawBalls(Graphics g){
      if(puyoLogic != null ){
          //the boolean running is to prevent null pointer
        for(int i=0;i<puyoLogic.numDroppingBalls;i++){   
            if(puyoLogic.droppingBalls[i]!=null)
            g.drawImage(puyoImages[color2ImageIndex(puyoLogic.droppingBalls[i].color)],
                        (puyoLogic.droppingBalls[i].x-1)*PuyoSettings.ballWidth,                        
                        (PuyoSettings.boardHeight-puyoLogic.droppingBalls[i].y)*PuyoSettings.ballHeight,
                        Graphics.TOP | Graphics.LEFT);
        }
        for(int i=1;i<PuyoSettings.boardWidth+1;i++)
            for(int j=1;j<PuyoSettings.boardHeight+1;j++)
                if(puyoLogic.accumBalls[i][j] != puyoLogic.puyoEmpty){
                    g.drawImage(puyoImages[color2ImageIndex(puyoLogic.accumBalls[i][j])],
                        (i-1)*PuyoSettings.ballWidth,
                        (PuyoSettings.boardHeight-j)*PuyoSettings.ballHeight,
                        Graphics.TOP | Graphics.LEFT);
                }
      }
  }

// When the game canvas is hidden, stop the thread.

  protected void hideNotify(){
      thread = null;
  }

  /** When the canvas is shown, start a thread to
  *run the game loop.
  */
  protected void showNotify(){
     thread = new Thread( this );
     thread.start();
  }
  
  private void delay(int mseconds){
       try {
	   Thread.currentThread().sleep(mseconds);
       } catch(Exception e) {}
  }
  /**pause switch*/
  public void changePauseState(){
       paused = !paused;
       if( paused ){
           pauseSound();
       }
       else{
           try {
	     player.start();
	   } catch (Exception ex) {}              
       }
  }  
  
  public void run() {
     Thread thisThread = Thread.currentThread();
     while(thread == thisThread){      
       updateGameScreen(getGraphics()); 
       delay(timeInterval);
       if(!paused && puyoLogic.drop()){ 
            updateGameScreen(getGraphics());             
            while(puyoLogic.checkAccumBalls()){ 
                 delay(2*timeInterval);  
                 try {
                     if(wavPlayer != null)
                        wavPlayer.start();
                 } catch(Exception e) {}
                 updateGameScreen(getGraphics()); 
                 while(puyoLogic.freeFalling());
                 try {
		     Thread.currentThread().sleep(30);
		 } catch(Exception e) {}
                 updateGameScreen(getGraphics()); 
            }
                      
            if(!puyoLogic.initDroppingBalls()) {
                 showGameOver = true;
                 removeCommand(PuyoMIDlet.pauseCommand);
                 removeCommand(PuyoMIDlet.resumeCommand);
                 updateGameScreen(getGraphics());
                 delay(2500);
                 notifyScoreObserver(); 
                 break;            
            }                  
        }        
        updateGameScreen(getGraphics());  
        delay(timeInterval);         
    }
  } 

    public boolean notifyScoreObserver() {
        pauseSound(); //we take this chance to pause the sound
        if(observer!=null){
           return observer.notifyScore(puyoLogic.getScore(), (byte)0); //now level is not implemented, simply set to 0
        }
        return false;
    }

    public void registerObserver(Observer anObserver) {
        observer = anObserver;
    }
  
    public void closeSound(){
        try {
	    if (player != null) {
		player.close();
                player = null;
            }
            if (wavPlayer != null) {
		wavPlayer.close();
                wavPlayer = null;
            }
	} catch (Exception ex) {}   
    }
    
    public void pauseSound(){
       try {
	    if (player != null){
		player.stop();               
            }
	} catch (MediaException ex){}
    }
    
     private void changeVolume(int diff) {
	VolumeControl vc;

	if ( player != null) {
	    vc = (VolumeControl) player.getControl("VolumeControl");
	    if (vc != null) {
		int cv = vc.getLevel();
                if(cv==0 && diff < 0 || cv==100 && diff>0) return;
                cv += diff;
                puyoConfig.setSoundVolume((short)cv);
		cv = vc.setLevel(cv);               	
	    }
            vc = (VolumeControl) wavPlayer.getControl("VolumeControl");	    
            if (vc != null) {
		int cv = vc.getLevel();
                if(cv==0 && diff < 0 || cv==100 && diff>0) return;
                cv += diff;                
		cv = vc.setLevel(cv);               	
	    }
	}
    }    
} //class end
